//
//  ShaderProgram.cpp
//  GSEngine
//
//  Created by Гукун Александр on 2/4/14.
//  Copyright (c) 2014 SNK. All rights reserved.
//

#include "ShaderProgram.h"
#include <OpenGLES/ES3/gl.h>
#include <OpenGLES/ES3/glext.h>

namespace GSEngine
{
    ShaderProgram::ShaderProgram()
    {
        loadDefaultSources();
        initShaders();
    }
    ShaderProgram::~ShaderProgram()
    {
    }
    void ShaderProgram::loadDefaultSources()
    {
        m_vsSource =
        "attribute vec3 coord;\n"
        "attribute vec2 textCoord;\n"
        "uniform mat4 viewMatrix;\n"
        "uniform mat4 worldMatrix;\n"
        "uniform mat4 projMatrix;\n"
        "varying vec2 texture_coords;\n"
        "void main() {\n"
        "   texture_coords = textCoord;\n"
        "  gl_Position = projMatrix * viewMatrix * worldMatrix * vec4(coord, 1.0);\n"
        "}\n";
        
        m_fsSource =
        "precision highp float;\n"
        "varying vec2 texture_coords;\n"
        "uniform sampler2D g_texture;\n"
        "void main() {\n"
        "   vec4 texColor = texture2D(g_texture, texture_coords);\n"
        "   gl_FragColor = texColor;\n"
        "}\n";
    }
    unsigned int ShaderProgram::getProgram()
    {
        return m_program;
    }
    void ShaderProgram::initShaders()
    {
        m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(m_vertexShader, 1, &m_vsSource, NULL);
        glCompileShader(m_vertexShader);
        int status;
        glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, &status);
        if (status != GL_TRUE) {
            char errorMsg[2048] = {};
            glGetShaderInfoLog(m_vertexShader, sizeof(errorMsg), NULL, errorMsg);
            std::cout << "\nERROR.. vertex shader compile status: " << status << " -> " << errorMsg << "\n";
        }
        
        m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(m_fragmentShader, 1, &m_fsSource, NULL);
        glCompileShader(m_fragmentShader);
        glGetShaderiv(m_fragmentShader, GL_COMPILE_STATUS, &status);
        if (status != GL_TRUE) {
            char errorMsg[2048] = {};
            glGetShaderInfoLog(m_fragmentShader, sizeof(errorMsg), NULL, errorMsg);
            std::cout << "\nERROR.. fragment shader compile status: " << status << "  " << errorMsg << "\n";
        }
        
        m_program = glCreateProgram();
        glAttachShader(m_program, m_vertexShader);
        glAttachShader(m_program, m_fragmentShader);
        
        glLinkProgram(m_program);
        
        int link_ok;
        glGetProgramiv(m_program, GL_LINK_STATUS, &link_ok);
        if (!link_ok)
            std::cout << "\nATTEMPTION!!! ERROR ATTACH SHADERS...\n";
    }
    void ShaderProgram::deleteShaders()
    {
        glDeleteShader(m_vertexShader);
        glDeleteShader(m_fragmentShader);
        glDeleteProgram(m_program);
        m_vertexShader = NULL;
        m_fragmentShader = NULL;
        m_program = NULL;
    }
    void ShaderProgram::setShader(EShaderType type, const char* source)
    {
        switch (type) {
            case EVertexShader:
                m_vsSource = source;
                deleteShaders();
                initShaders();
                break;

            case EFragmentShader:
                m_fsSource = source;
                deleteShaders();
                initShaders();
                break;
                
            case ETextureShader:
                // reserved for future geometry creating
                break;

            default:
                break;
        }
    }
}